home *** CD-ROM | disk | FTP | other *** search
/ Skunkware 5 / Skunkware 5.iso / src / X11 / xmbase-grok-1.2 / mainwin.c < prev    next >
C/C++ Source or Header  |  1995-06-25  |  49KB  |  1,584 lines

  1. /*
  2.  * Create the main window and everything in it.
  3.  *
  4.  *    create_mainwindow(toplvl)    Create all widgets in main window
  5.  *    print_info_line()        print database status into main window
  6.  *    remake_dbase_pulldown()        put known forms into dbase pulldown
  7.  *    remake_section_pulldown()    put known sections into sect pulldown
  8.  *    remake_query_pulldown()        put predefined queries into pulldown
  9.  *    remake_sort_pulldown()        put sort criteria into sort pulldown
  10.  *    switch_form(formname)        switch mainwindow to new form
  11.  *    search_cards(card,string)    do a query for an expression or string
  12.  */
  13.  
  14. #include "config.h"
  15. #include <X11/Xos.h>
  16. #include <stdlib.h>
  17. #ifdef DIRECT
  18. #include <sys/dir.h>
  19. #define  dirent direct
  20. #else
  21. #include <dirent.h>
  22. #endif
  23. #include <Xm/Xm.h>
  24. #include <Xm/MainW.h>
  25. #include <Xm/Separator.h>
  26. #include <Xm/RowColumn.h>
  27. #include <Xm/Form.h>
  28. #include <Xm/Frame.h>
  29. #include <Xm/LabelP.h>
  30. #include <Xm/ArrowBP.h>
  31. #include <Xm/ArrowBG.h>
  32. #include <Xm/Text.h>
  33. #include <Xm/PushBP.h>
  34. #include <Xm/PushBG.h>
  35. #include <X11/cursorfont.h>
  36. #include "grok.h"
  37. #include "form.h"
  38. #include "proto.h"
  39.  
  40. #define OFF        16        /* margin around summary and card */
  41. #define NHIST        20        /* remember this many search strings */
  42.  
  43. #ifndef LIB
  44. #define LIB "/usr/local/lib"
  45. #endif
  46.  
  47. static int append_to_dbase_pulldown(long, char *);
  48. static void append_search_string(char *);
  49. static void file_pulldown    (Widget, int, XmToggleButtonCallbackStruct *);
  50. static void newform_pulldown    (Widget, int, XmToggleButtonCallbackStruct *);
  51. static void help_pulldown    (Widget, int, XmToggleButtonCallbackStruct *);
  52. static void dbase_pulldown    (Widget, int, XmToggleButtonCallbackStruct *);
  53. static void section_pulldown    (Widget, int, XmToggleButtonCallbackStruct *);
  54. static void sect_callback    (Widget, int, XmToggleButtonCallbackStruct *);
  55. static void query_pulldown    (Widget, int, XmToggleButtonCallbackStruct *);
  56. static void sort_pulldown    (Widget, int, XmToggleButtonCallbackStruct *);
  57. static void search_callback    (Widget, int, XmToggleButtonCallbackStruct *);
  58. static void letter_callback    (Widget, int, XmToggleButtonCallbackStruct *);
  59. static void pos_callback    (Widget, int, XmToggleButtonCallbackStruct *);
  60. static void new_callback  (Widget, XtPointer, XmToggleButtonCallbackStruct *);
  61. static void dup_callback  (Widget, XtPointer, XmToggleButtonCallbackStruct *);
  62. static void del_callback  (Widget, XtPointer, XmToggleButtonCallbackStruct *);
  63.  
  64. extern Display        *display;    /* everybody uses the same server */
  65. extern GC        gc;        /* everybody uses this context */
  66. extern struct config    config;        /* global configuration data */
  67. extern Pixel        color[NCOLS];    /* colors: COL_* */
  68. extern Pixmap        pixmap[NPICS];    /* common symbols */
  69. extern XmFontList    fontlist[NFONTS];
  70. extern struct pref    pref;        /* global preferences */
  71. extern BOOL        restricted;    /* restricted mode, no form editor */
  72. extern int        col_sorted_by;    /* dbase is sorted by this column */
  73. extern int        errno;
  74. CARD             *curr_card;    /* card being displayed in main win, */
  75. char            *prev_form;    /* previous form name */
  76. Widget            mainwindow;    /* popup menus hang off main window */
  77. static Dimension    win_xs, win_ys;    /* size of main window w/o sum+card */
  78. static Widget        toplevel;    /* need to save this for icon name */
  79. static Widget        menubar;    /* menu bar in main window */
  80. static Widget        dbpulldown;    /* dbase pulldown menu widget */
  81. static Widget        sectpulldown;    /* sectn pulldown menu widget */
  82. static Widget        sortpulldown;    /* sort  pulldown menu widget */
  83. static Widget        qpulldown;    /* query pulldown menu widget */
  84. static Widget        sectpdcall;    /* button that calls section pulldn */
  85.  
  86. static Widget        form;        /* w_sect etc are on this form */
  87. static Widget        w_info;        /* info label, for "n found" */
  88. static Widget        w_search;    /* search string text widget */
  89. static Widget        w_prev;        /* search prev arrow */
  90. static Widget        w_next;        /* search next arrow */
  91.        Widget        w_summary;    /* form for summary table */
  92. static Widget        w_letter[28];    /* buttons for a,b,c,...,z,misc,* */
  93. static Widget        w_card;        /* form for card */
  94. static Widget        w_left;        /* button: previous card */
  95. static Widget        w_right;    /* button: next card */
  96. static Widget        w_new;        /* button: start a new card */
  97. static Widget        w_dup;        /* button: duplicate a card */
  98. static Widget        w_del;        /* button: delete current card */
  99. static Widget        w_sect;        /* button: section popup for card */
  100.  
  101.  
  102.  
  103. /*
  104.  * create the main window with a menu bar and empty forms for the summary
  105.  * and the card. The forms will be filled later. This routine is called
  106.  * once during startup, before the first switch_form().
  107.  */
  108.  
  109. void create_mainwindow(
  110.     Widget        toplvl)
  111. {
  112.     XmString    s[12];
  113.     Widget        fmenu, w, searchform;
  114.     Arg        args[15];
  115.     long        n, i, wid;
  116.     char        buf[10];
  117.  
  118.     toplevel = toplvl;
  119.     mainwindow = XtCreateManagedWidget("mainwindow",
  120.             xmMainWindowWidgetClass, toplevel, NULL, 0);
  121.  
  122.                             /*-- menu bar --*/
  123.     s[0] = XmStringCreateSimple("File");
  124.     s[1] = XmStringCreateSimple("Database");
  125.     s[2] = XmStringCreateSimple("Section");
  126.     s[3] = XmStringCreateSimple("Sort");
  127.     s[4] = XmStringCreateSimple("Query");
  128.     s[5] = XmStringCreateSimple("Help");
  129.     menubar = XmVaCreateSimpleMenuBar(mainwindow, "menubar",
  130.             FIX_MENUBAR
  131.             XmVaCASCADEBUTTON, s[0], 'F',
  132.             XmVaCASCADEBUTTON, s[1], 'D',
  133.             XmVaCASCADEBUTTON, s[2], 'e',
  134.             XmVaCASCADEBUTTON, s[3], 'S',
  135.             XmVaCASCADEBUTTON, s[4], 'Q',
  136.             XmVaCASCADEBUTTON, s[5], 'H',
  137.             NULL);
  138.     if (w = XtNameToWidget(menubar, "button_5"))
  139.         XtVaSetValues(menubar, XmNmenuHelpWidget, w, 0);
  140.     sectpdcall = XtNameToWidget(menubar, "button_2");
  141.     for (n=0; n < 5; n++)
  142.         XmStringFree(s[n]);
  143.  
  144.     s[0] = XmStringCreateSimple("Print...");
  145.     s[1] = XmStringCreateSimple("Preferences...");
  146.     s[2] = XmStringCreateSimple("Form editor");
  147.     s[3] = XmStringCreateSimple("About...");
  148.     s[4] = XmStringCreateSimple("Save");
  149.     s[5] = XmStringCreateSimple("Quit");
  150.     s[6] = XmStringCreateSimple("Rambo Quit");
  151.     s[7] = XmStringCreateSimple("Ctrl-P");
  152.     s[8] = XmStringCreateSimple("Ctrl-R");
  153.     s[9] = XmStringCreateSimple("Ctrl-S");
  154.     s[10]= XmStringCreateSimple("Ctrl-Q");
  155.     fmenu = XmVaCreateSimplePulldownMenu(menubar, "file", 0,
  156.             (XtCallbackProc)file_pulldown,
  157.             FIX_MENUBAR
  158.             XmVaPUSHBUTTON,    s[0], 'P',  "Ctrl<Key>P", s[7],
  159.             XmVaPUSHBUTTON,    s[1], 'r',  "Ctrl<Key>R", s[8],
  160.             XmVaCASCADEBUTTON, s[2], 'F',
  161.             XmVaPUSHBUTTON,    s[3], 'A',  NULL, NULL,
  162.             XmVaPUSHBUTTON,    s[4], 'S',  "Ctrl<Key>S", s[9],
  163.             XmVaPUSHBUTTON,    s[5], 'Q',  "Ctrl<Key>Q", s[10],
  164.             XmVaPUSHBUTTON,    s[6], NULL, NULL, NULL,
  165.             NULL);
  166. #ifdef XmNtearOffModel
  167.     XtVaSetValues(fmenu, XmNtearOffModel, XmTEAR_OFF_ENABLED, NULL);
  168. #endif
  169.     XtAddCallback(fmenu, XmNhelpCallback,
  170.             (XtCallbackProc)help_callback, (XtPointer)"pd_file");
  171.     for (n=0; n < 11; n++)
  172.         XmStringFree(s[n]);
  173.  
  174.     if (restricted && (w = XtNameToWidget(fmenu, "button_2")))
  175.         XtVaSetValues(w, XmNsensitive, FALSE, 0);
  176.  
  177.     s[0] = XmStringCreateSimple("Edit current form...");
  178.     s[1] = XmStringCreateSimple("Create new form from scratch...");
  179.     s[2] = XmStringCreateSimple("Create, use current as template...");
  180.     fmenu = XmVaCreateSimplePulldownMenu(fmenu, "newform", 2,
  181.             (XtCallbackProc)newform_pulldown,
  182.             FIX_MENUBAR
  183.             XmVaPUSHBUTTON,    s[0], 'E',  NULL, NULL,
  184.             XmVaPUSHBUTTON,    s[1], 'C',  NULL, NULL,
  185.             XmVaPUSHBUTTON,    s[2], 't',  NULL, NULL,
  186.             NULL);
  187. #ifdef XmNtearOffModel
  188.     XtVaSetValues(fmenu, XmNtearOffModel, XmTEAR_OFF_ENABLED, NULL);
  189. #endif
  190.     XtAddCallback(fmenu, XmNhelpCallback,
  191.             (XtCallbackProc)help_callback, (XtPointer)"pd_file");
  192.     for (n=0; n < 3; n++)
  193.         XmStringFree(s[n]);
  194.  
  195.     dbpulldown = XmVaCreateSimplePulldownMenu(menubar, "dbase", 1,
  196.             (XtCallbackProc)dbase_pulldown,
  197.             FIX_MENUBAR
  198.             NULL);
  199.     XtAddCallback(dbpulldown, XmNhelpCallback,
  200.             (XtCallbackProc)help_callback, (XtPointer)"pd_dbase");
  201.  
  202.     sectpulldown = XmVaCreateSimplePulldownMenu(menubar, "section", 2,
  203.             (XtCallbackProc)section_pulldown,
  204.             FIX_MENUBAR
  205.             NULL);
  206.     XtAddCallback(sectpulldown, XmNhelpCallback,
  207.             (XtCallbackProc)help_callback,(XtPointer)"pd_section");
  208.  
  209.     sortpulldown = XmVaCreateSimplePulldownMenu(menubar, "sort", 3,
  210.             (XtCallbackProc)sort_pulldown,
  211.             FIX_MENUBAR
  212.             NULL);
  213.     XtAddCallback(sortpulldown, XmNhelpCallback,
  214.             (XtCallbackProc)help_callback, (XtPointer)"pd_sort");
  215.  
  216.     qpulldown = XmVaCreateSimplePulldownMenu(menubar, "query", 4,
  217.             (XtCallbackProc)query_pulldown,
  218.             FIX_MENUBAR
  219.             NULL);
  220.     XtAddCallback(qpulldown, XmNhelpCallback,
  221.             (XtCallbackProc)help_callback, (XtPointer)"pd_query");
  222.  
  223.     s[0] = XmStringCreateSimple("On context");
  224.     s[1] = XmStringCreateSimple("Current database");
  225.     s[2] = XmStringCreateSimple("Introduction");
  226.     s[3] = XmStringCreateSimple("Getting help");
  227.     s[4] = XmStringCreateSimple("Troubleshooting");
  228.     s[5] = XmStringCreateSimple("Files and programs");
  229.     s[6] = XmStringCreateSimple("Expression grammar");
  230.     s[7] = XmStringCreateSimple("Variables and X Resources");
  231.     s[8] = XmStringCreateSimple("Ctrl-H");
  232.     s[9] = XmStringCreateSimple("Ctrl-D");
  233.     s[10]= XmStringCreateSimple("Ctrl-G");
  234.     fmenu = XmVaCreateSimplePulldownMenu(menubar, "help", 5,
  235.             (XtCallbackProc)help_pulldown,
  236.             FIX_MENUBAR
  237.             XmVaPUSHBUTTON, s[0], 'C',  "Ctrl<Key>H", s[8],
  238.             XmVaPUSHBUTTON, s[1], 'D',  "Ctrl<Key>D", s[9],
  239.             XM_VA_SEPARATOR
  240.             XmVaPUSHBUTTON, s[2], 'I',  NULL, NULL,
  241.             XmVaPUSHBUTTON, s[3], 'G',  NULL, NULL,
  242.             XmVaPUSHBUTTON, s[4], 'T',  NULL, NULL,
  243.             XmVaPUSHBUTTON, s[5], 'F',  NULL, NULL,
  244.             XmVaPUSHBUTTON, s[6], 'E',  "Ctrl<Key>G", s[10],
  245.             XmVaPUSHBUTTON, s[7], 'V',  NULL, NULL,
  246.             NULL);
  247. #ifdef XmNtearOffModel
  248.     XtVaSetValues(fmenu, XmNtearOffModel, XmTEAR_OFF_ENABLED, NULL);
  249. #endif
  250.     for (n=0; n < 10; n++)
  251.         XmStringFree(s[n]);
  252.  
  253.     form = XtCreateWidget("mainform", xmFormWidgetClass,
  254.             mainwindow, NULL, 0);
  255.                             /*-- search string --*/
  256.     n = 0;
  257.     XtSetArg(args[n], XmNtopAttachment,    XmATTACH_FORM);        n++;
  258.     XtSetArg(args[n], XmNtopOffset,        10);            n++;
  259.     XtSetArg(args[n], XmNleftAttachment,    XmATTACH_FORM);        n++;
  260.     XtSetArg(args[n], XmNleftOffset,    OFF);            n++;
  261.     XtSetArg(args[n], XmNrightAttachment,    XmATTACH_FORM);        n++;
  262.     XtSetArg(args[n], XmNrightOffset,    OFF);            n++;
  263.     searchform = XtCreateManagedWidget("searchform", xmFormWidgetClass,
  264.             form, args, n);
  265.     XtAddCallback(searchform, XmNhelpCallback,
  266.             (XtCallbackProc)help_callback, (XtPointer)"search");
  267.  
  268.     n = 0;
  269.     XtSetArg(args[n], XmNtopAttachment,    XmATTACH_FORM);        n++;
  270.     XtSetArg(args[n], XmNbottomAttachment,    XmATTACH_FORM);        n++;
  271.     XtSetArg(args[n], XmNrightAttachment,    XmATTACH_FORM);        n++;
  272.     w = XtCreateManagedWidget("Search", xmPushButtonWidgetClass,
  273.             searchform, args, n);
  274.     XtAddCallback(w, XmNactivateCallback,
  275.             (XtCallbackProc)search_callback, (XtPointer)0);
  276.     XtAddCallback(w, XmNhelpCallback,
  277.             (XtCallbackProc)help_callback, (XtPointer)"search");
  278.  
  279.     n = 0;
  280.     XtSetArg(args[n], XmNtopAttachment,    XmATTACH_FORM);        n++;
  281.     XtSetArg(args[n], XmNtopOffset,        3);            n++;
  282.     XtSetArg(args[n], XmNbottomAttachment,    XmATTACH_FORM);        n++;
  283.     XtSetArg(args[n], XmNbottomOffset,    3);            n++;
  284.     XtSetArg(args[n], XmNrightAttachment,    XmATTACH_WIDGET);    n++;
  285.     XtSetArg(args[n], XmNrightWidget,    w);            n++;
  286.     XtSetArg(args[n], XmNrightOffset,    10);            n++;
  287.     XtSetArg(args[n], XmNarrowDirection,    XmARROW_RIGHT);        n++;
  288.     XtSetArg(args[n], XmNsensitive,        FALSE);            n++;
  289.     XtSetArg(args[n], XmNforeground,    color[COL_BACK]);    n++;
  290.     w = w_next = XtCreateManagedWidget("next", xmArrowButtonWidgetClass,
  291.             searchform, args, n);
  292.     XtAddCallback(w, XmNactivateCallback,
  293.             (XtCallbackProc)search_callback, (XtPointer)1);
  294.     XtAddCallback(w, XmNhelpCallback,
  295.             (XtCallbackProc)help_callback, (XtPointer)"search");
  296.  
  297.     n = 0;
  298.     XtSetArg(args[n], XmNtopAttachment,    XmATTACH_FORM);        n++;
  299.     XtSetArg(args[n], XmNtopOffset,        3);            n++;
  300.     XtSetArg(args[n], XmNbottomAttachment,    XmATTACH_FORM);        n++;
  301.     XtSetArg(args[n], XmNbottomOffset,    3);            n++;
  302.     XtSetArg(args[n], XmNrightAttachment,    XmATTACH_WIDGET);    n++;
  303.     XtSetArg(args[n], XmNrightWidget,    w);            n++;
  304.     XtSetArg(args[n], XmNarrowDirection,    XmARROW_LEFT);        n++;
  305.     XtSetArg(args[n], XmNsensitive,        FALSE);            n++;
  306.     XtSetArg(args[n], XmNforeground,    color[COL_BACK]);    n++;
  307.     w = w_prev = XtCreateManagedWidget("prev", xmArrowButtonWidgetClass,
  308.             searchform, args, n);
  309.     XtAddCallback(w, XmNactivateCallback,
  310.             (XtCallbackProc)search_callback, (XtPointer)-1);
  311.     XtAddCallback(w, XmNhelpCallback,
  312.             (XtCallbackProc)help_callback, (XtPointer)"search");
  313.  
  314.     n = 0;
  315.     XtSetArg(args[n], XmNtopAttachment,    XmATTACH_FORM);        n++;
  316.     XtSetArg(args[n], XmNbottomAttachment,    XmATTACH_FORM);        n++;
  317.     XtSetArg(args[n], XmNleftAttachment,    XmATTACH_FORM);        n++;
  318.     XtSetArg(args[n], XmNfontList,        fontlist[FONT_HELV]);    n++;
  319.     w = XtCreateManagedWidget("Search:", xmLabelWidgetClass,
  320.             searchform, args, n);
  321.  
  322.     n = 0;
  323.     XtSetArg(args[n], XmNtopAttachment,    XmATTACH_FORM);        n++;
  324.     XtSetArg(args[n], XmNbottomAttachment,    XmATTACH_FORM);        n++;
  325.     XtSetArg(args[n], XmNleftAttachment,    XmATTACH_WIDGET);    n++;
  326.     XtSetArg(args[n], XmNleftWidget,    w);            n++;
  327.     XtSetArg(args[n], XmNleftOffset,    OFF);            n++;
  328.     XtSetArg(args[n], XmNrightAttachment,    XmATTACH_WIDGET);    n++;
  329.     XtSetArg(args[n], XmNrightWidget,    w_prev);        n++;
  330.     XtSetArg(args[n], XmNrightOffset,    OFF);            n++;
  331.     XtSetArg(args[n], XmNpendingDelete,    True);            n++;
  332.     XtSetArg(args[n], XmNbackground,    color[COL_TEXTBACK]);    n++;
  333.     XtSetArg(args[n], XmNfontList,        fontlist[FONT_HELV]);    n++;
  334.     w_search = XtCreateManagedWidget(" ", xmTextWidgetClass,
  335.             searchform, args, n);
  336.     XtAddCallback(w_search, XmNactivateCallback,
  337.             (XtCallbackProc)search_callback, NULL);
  338.     XtAddCallback(w_search, XmNhelpCallback,
  339.             (XtCallbackProc)help_callback, (XtPointer)"search");
  340.  
  341.                             /*-- info line --*/
  342.     n = 0;
  343.     XtSetArg(args[n], XmNtopAttachment,    XmATTACH_WIDGET);    n++;
  344.     XtSetArg(args[n], XmNtopWidget,        searchform);        n++;
  345.     XtSetArg(args[n], XmNtopOffset,        10);            n++;
  346.     XtSetArg(args[n], XmNleftAttachment,    XmATTACH_FORM);        n++;
  347.     XtSetArg(args[n], XmNleftOffset,    OFF);            n++;
  348.     XtSetArg(args[n], XmNrightAttachment,    XmATTACH_FORM);        n++;
  349.     XtSetArg(args[n], XmNrightOffset,    OFF);            n++;
  350.     XtSetArg(args[n], XmNalignment,     XmALIGNMENT_BEGINNING);    n++;
  351.     XtSetArg(args[n], XmNfontList,        fontlist[FONT_HELV]);    n++;
  352.     w_info = XtCreateManagedWidget(" ", xmLabelWidgetClass,
  353.             form, args, n);
  354.     XtAddCallback(w_info, XmNhelpCallback,
  355.             (XtCallbackProc)help_callback, (XtPointer)"info");
  356.  
  357.                             /*-- buttons --*/
  358.     n = 0;
  359.     XtSetArg(args[n], XmNbottomAttachment,    XmATTACH_FORM);        n++;
  360.     XtSetArg(args[n], XmNbottomOffset,    8);            n++;
  361.     XtSetArg(args[n], XmNleftAttachment,    XmATTACH_FORM);        n++;
  362.     XtSetArg(args[n], XmNleftOffset,    OFF);            n++;
  363.     XtSetArg(args[n], XmNwidth,        30);            n++;
  364.     XtSetArg(args[n], XmNheight,        30);            n++;
  365.     XtSetArg(args[n], XmNlabelType,        XmPIXMAP);        n++;
  366.     XtSetArg(args[n], XmNlabelPixmap,    pixmap[PIC_LEFT]);    n++;
  367.     XtSetArg(args[n], XmNsensitive,        False);            n++;
  368.     XtSetArg(args[n], XmNhighlightThickness,1);            n++;
  369.     w_left = XtCreateManagedWidget("Left", xmPushButtonWidgetClass,
  370.             form, args, n);
  371.     XtAddCallback(w_left, XmNactivateCallback,
  372.             (XtCallbackProc)pos_callback, (XtPointer)-1);
  373.     XtAddCallback(w_left, XmNhelpCallback,
  374.             (XtCallbackProc)help_callback, (XtPointer)"pos");
  375.  
  376.     n = 0;
  377.     XtSetArg(args[n], XmNbottomAttachment,    XmATTACH_FORM);        n++;
  378.     XtSetArg(args[n], XmNbottomOffset,    8);            n++;
  379.     XtSetArg(args[n], XmNleftAttachment,    XmATTACH_WIDGET);    n++;
  380.     XtSetArg(args[n], XmNleftWidget,    w_left);        n++;
  381.     XtSetArg(args[n], XmNleftOffset,    4);            n++;
  382.     XtSetArg(args[n], XmNwidth,        30);            n++;
  383.     XtSetArg(args[n], XmNheight,        30);            n++;
  384.     XtSetArg(args[n], XmNlabelType,        XmPIXMAP);        n++;
  385.     XtSetArg(args[n], XmNlabelPixmap,    pixmap[PIC_RIGHT]);    n++;
  386.     XtSetArg(args[n], XmNsensitive,        False);            n++;
  387.     XtSetArg(args[n], XmNhighlightThickness,1);            n++;
  388.     w_right = XtCreateManagedWidget("Right", xmPushButtonWidgetClass,
  389.             form, args, n);
  390.     XtAddCallback(w_right, XmNactivateCallback,
  391.             (XtCallbackProc)pos_callback, (XtPointer)1);
  392.     XtAddCallback(w_right, XmNhelpCallback,
  393.             (XtCallbackProc)help_callback, (XtPointer)"pos");
  394.  
  395.     n = 0;
  396.     XtSetArg(args[n], XmNbottomAttachment,    XmATTACH_FORM);        n++;
  397.     XtSetArg(args[n], XmNbottomOffset,    8);            n++;
  398.     XtSetArg(args[n], XmNleftAttachment,    XmATTACH_WIDGET);    n++;
  399.     XtSetArg(args[n], XmNleftWidget,    w_right);        n++;
  400.     XtSetArg(args[n], XmNleftOffset,    16);            n++;
  401.     XtSetArg(args[n], XmNwidth,        60);            n++;
  402.     XtSetArg(args[n], XmNsensitive,        False);            n++;
  403.     XtSetArg(args[n], XmNhighlightThickness,1);            n++;
  404.     w_new = XtCreateManagedWidget("New", xmPushButtonWidgetClass,
  405.             form, args, n);
  406.     XtAddCallback(w_new, XmNactivateCallback,
  407.             (XtCallbackProc)new_callback, (XtPointer)0);
  408.     XtAddCallback(w_new, XmNhelpCallback,
  409.             (XtCallbackProc)help_callback, (XtPointer)"new");
  410.  
  411.     n = 0;
  412.     XtSetArg(args[n], XmNbottomAttachment,    XmATTACH_FORM);        n++;
  413.     XtSetArg(args[n], XmNbottomOffset,    8);            n++;
  414.     XtSetArg(args[n], XmNleftAttachment,    XmATTACH_WIDGET);    n++;
  415.     XtSetArg(args[n], XmNleftWidget,    w_new);            n++;
  416.     XtSetArg(args[n], XmNleftOffset,    8);            n++;
  417.     XtSetArg(args[n], XmNwidth,        60);            n++;
  418.     XtSetArg(args[n], XmNsensitive,        False);            n++;
  419.     XtSetArg(args[n], XmNhighlightThickness,1);            n++;
  420.     w_dup = XtCreateManagedWidget("Dup", xmPushButtonWidgetClass,
  421.             form, args, n);
  422.     XtAddCallback(w_dup, XmNactivateCallback,
  423.             (XtCallbackProc)dup_callback, (XtPointer)0);
  424.     XtAddCallback(w_dup, XmNhelpCallback,
  425.             (XtCallbackProc)help_callback, (XtPointer)"dup");
  426.  
  427.     n = 0;
  428.     XtSetArg(args[n], XmNbottomAttachment,    XmATTACH_FORM);        n++;
  429.     XtSetArg(args[n], XmNbottomOffset,    8);            n++;
  430.     XtSetArg(args[n], XmNleftAttachment,    XmATTACH_WIDGET);    n++;
  431.     XtSetArg(args[n], XmNleftWidget,    w_dup);            n++;
  432.     XtSetArg(args[n], XmNleftOffset,    8);            n++;
  433.     XtSetArg(args[n], XmNwidth,        60);            n++;
  434.     XtSetArg(args[n], XmNsensitive,        False);            n++;
  435.     XtSetArg(args[n], XmNhighlightThickness,1);            n++;
  436.     w_del = XtCreateManagedWidget("Delete", xmPushButtonWidgetClass,
  437.             form, args, n);
  438.     XtAddCallback(w_del, XmNactivateCallback,
  439.             (XtCallbackProc)del_callback, (XtPointer)0);
  440.     XtAddCallback(w_del, XmNhelpCallback,
  441.             (XtCallbackProc)help_callback, (XtPointer)"del");
  442.  
  443.     n = 0;
  444.     XtSetArg(args[n], XmNbottomAttachment,    XmATTACH_FORM);        n++;
  445.     XtSetArg(args[n], XmNbottomOffset,    8);            n++;
  446.     XtSetArg(args[n], XmNrightAttachment,    XmATTACH_FORM);        n++;
  447.     XtSetArg(args[n], XmNrightOffset,    OFF);            n++;
  448.     XtSetArg(args[n], XmNwidth,        60);            n++;
  449.     XtSetArg(args[n], XmNhighlightThickness,1);            n++;
  450.     w = XtCreateManagedWidget("Help", xmPushButtonWidgetClass,
  451.             form, args, n);
  452.     XtAddCallback(w, XmNactivateCallback,
  453.             (XtCallbackProc)help_callback, (XtPointer)"card");
  454.     XtAddCallback(w, XmNhelpCallback,
  455.             (XtCallbackProc)help_callback, (XtPointer)"card");
  456.  
  457.                             /*-- summary --*/
  458.     n = 0;
  459.     XtSetArg(args[n], XmNwidth,        400);            n++;
  460.     XtSetArg(args[n], XmNtopAttachment,    XmATTACH_WIDGET);    n++;
  461.     XtSetArg(args[n], XmNtopWidget,        w_info);        n++;
  462.     XtSetArg(args[n], XmNtopOffset,        4);            n++;
  463.     XtSetArg(args[n], XmNleftAttachment,    XmATTACH_FORM);        n++;
  464.     XtSetArg(args[n], XmNleftOffset,    OFF);            n++;
  465.     XtSetArg(args[n], XmNrightAttachment,    XmATTACH_FORM);        n++;
  466.     XtSetArg(args[n], XmNrightOffset,    OFF);            n++;
  467.     w = w_summary = XtCreateManagedWidget("summform", xmFormWidgetClass,
  468.             form, args, n);
  469.     XtAddCallback(w, XmNhelpCallback,
  470.             (XtCallbackProc)help_callback, (XtPointer)"summary");
  471.  
  472.                             /*-- letters --*/
  473.     if (pref.letters) {
  474.      n = 0;
  475.      XtSetArg(args[n], XmNtopAttachment,    XmATTACH_WIDGET);    n++;
  476.      XtSetArg(args[n], XmNtopWidget,    w_summary);        n++;
  477.      XtSetArg(args[n], XmNtopOffset,    5);            n++;
  478.      XtSetArg(args[n], XmNleftAttachment,    XmATTACH_FORM);        n++;
  479.      XtSetArg(args[n], XmNleftOffset,    OFF+2);            n++;
  480.      XtSetArg(args[n], XmNrightAttachment,    XmATTACH_FORM);        n++;
  481.      XtSetArg(args[n], XmNrightOffset,    OFF);            n++;
  482.      w = XtCreateManagedWidget("letters", xmFormWidgetClass,
  483.             form, args, n);
  484.      XtAddCallback(w, XmNhelpCallback,
  485.              (XtCallbackProc)help_callback,(XtPointer)"letters");
  486.  
  487.      for (i=0; i < 28; i++) {
  488.       wid = strlen_in_pixels("W", FONT_STD);
  489.       sprintf(buf, i < 26 ? "%c" : i==26 ? "misc" : "all", i+'A');
  490.       n = 0;
  491.       if (i == 0) {
  492.        XtSetArg(args[n], XmNleftAttachment,    XmATTACH_FORM);        n++;
  493.       } else {
  494.        XtSetArg(args[n], XmNleftAttachment,    XmATTACH_WIDGET);    n++;
  495.        XtSetArg(args[n], XmNleftWidget,    w_letter[i-1]);        n++;
  496.       }
  497.       if (i < 26) {
  498.        XtSetArg(args[n], XmNwidth,        wid + 6);        n++;
  499.       }
  500.       XtSetArg(args[n], XmNheight,        wid + 6);        n++;
  501.       XtSetArg(args[n], XmNleftOffset,    0);            n++;
  502.       XtSetArg(args[n], XmNshadowThickness,    2);            n++;
  503.       XtSetArg(args[n], XmNtopAttachment,    XmATTACH_FORM);        n++;
  504.       XtSetArg(args[n], XmNbottomAttachment,XmATTACH_FORM);        n++;
  505.       XtSetArg(args[n], XmNhighlightThickness, 0);            n++;
  506.       w_letter[i] = XtCreateManagedWidget(buf, xmPushButtonWidgetClass,
  507.                     w, args, n);
  508.       XtAddCallback(w_letter[i], XmNactivateCallback,
  509.             (XtCallbackProc)letter_callback, (XtPointer)i);
  510.       XtAddCallback(w_letter[i], XmNhelpCallback,
  511.              (XtCallbackProc) help_callback, (XtPointer)"letters");
  512.      }
  513.     }
  514.                             /*-- card --*/
  515.     n = 0;
  516.     XtSetArg(args[n], XmNtopAttachment,    XmATTACH_WIDGET);    n++;
  517.     XtSetArg(args[n], XmNtopWidget,        w);            n++;
  518.     XtSetArg(args[n], XmNtopOffset,        OFF);            n++;
  519.     XtSetArg(args[n], XmNleftAttachment,    XmATTACH_FORM);        n++;
  520.     XtSetArg(args[n], XmNrightAttachment,    XmATTACH_FORM);        n++;
  521.     w = XtCreateManagedWidget(" ", xmSeparatorWidgetClass,
  522.             form, args, n);
  523.     n = 0;
  524.     XtSetArg(args[n], XmNwidth,        400);            n++;
  525.     XtSetArg(args[n], XmNheight,        6);            n++;
  526.     XtSetArg(args[n], XmNtopAttachment,    XmATTACH_WIDGET);    n++;
  527.     XtSetArg(args[n], XmNtopWidget,        w);            n++;
  528.     XtSetArg(args[n], XmNtopOffset,        OFF);            n++;
  529.     XtSetArg(args[n], XmNbottomAttachment,    XmATTACH_WIDGET);    n++;
  530.     XtSetArg(args[n], XmNbottomWidget,    w_new);            n++;
  531.     XtSetArg(args[n], XmNbottomOffset,    8);            n++;
  532.     XtSetArg(args[n], XmNleftAttachment,    XmATTACH_FORM);        n++;
  533.     XtSetArg(args[n], XmNleftOffset,    OFF);            n++;
  534.     XtSetArg(args[n], XmNrightAttachment,    XmATTACH_FORM);        n++;
  535.     XtSetArg(args[n], XmNrightOffset,    OFF);            n++;
  536.     w_card = XtCreateManagedWidget("cardform", xmFormWidgetClass,
  537.             form, args, n);
  538.     XtAddCallback(w_card, XmNhelpCallback,
  539.             (XtCallbackProc)help_callback, (XtPointer)"card");
  540.  
  541.     XtManageChild(form);
  542.     XtManageChild(menubar);
  543.     create_summary_menu(curr_card, w_summary, mainwindow);
  544.     remake_dbase_pulldown();
  545.     remake_section_pulldown();
  546.     remake_query_pulldown();
  547.     remake_sort_pulldown();
  548. }
  549.  
  550.  
  551. /*
  552.  * resize the main window such that there is enough room for the summary
  553.  * and the card. During startup, create_mainwindow() has stored the size
  554.  * without summary and card, now add enough space for the new summary and
  555.  * card and resize the window.
  556.  */
  557.  
  558. void resize_mainwindow(void)
  559. {
  560.     Arg        args[2];
  561.     Dimension    xs=0, ys=0;
  562.  
  563.     if (!win_ys) {
  564.         XtSetArg(args[0], XmNwidth,  &win_xs);
  565.         XtSetArg(args[1], XmNheight, &win_ys);
  566.         XtGetValues(mainwindow, args, 2);
  567.         win_ys -= 1;
  568.     }
  569.     if (curr_card && curr_card->form) {
  570.         xs = pref.scale * curr_card->form->xs + 2;
  571.         ys = pref.scale * curr_card->form->ys + 2;
  572.     }
  573.     if (win_xs > xs) xs = win_xs;
  574.     XtSetArg(args[0], XmNwidth,  xs + 2*2 + 2*16);
  575.     XtSetArg(args[1], XmNheight, win_ys + ys + 2*3);
  576.     XtSetValues(XtParent(mainwindow), args, 2);
  577. }
  578.  
  579.  
  580. /*
  581.  * print some enlightening info about the database into the info line between
  582.  * the search string and the summary. This is called whenever something
  583.  * changes: either the database was modified, or saved (modified is turned
  584.  * off), or a search is done.
  585.  */
  586.  
  587. void print_info_line(void)
  588. {
  589.     char        buf[256];
  590.     register CARD    *card = curr_card;
  591.     register DBASE    *dbase;
  592.     int        s, n;
  593.  
  594.     if (!display)
  595.         return;
  596.     if (!card || !card->dbase || !card->form || !card->form->name)
  597.         strcpy(buf, "No database");
  598.     else {
  599.         dbase = card->dbase;
  600.         s = dbase->nsects > 1 ? dbase->currsect : -1;
  601.         n = s >= 0 ? dbase->sect[s].nrows : dbase->nrows;
  602.         if (!dbase->nrows)
  603.             strcpy(buf, "No cards");
  604.         else
  605.             sprintf(buf, "%s: %d of %d card%s", card->form->name,
  606.                     card->nquery, n, n==1 ? "" : "s");
  607.         if (s >= 0) {
  608.             strcat(buf, " in section ");
  609.             strcat(buf, section_name(dbase, s));
  610.         }
  611.         if (s >= 0 ? dbase->sect[s].rdonly : dbase->rdonly)
  612.             strcat(buf, " (read only)");
  613.         if (s >= 0 ? dbase->sect[s].modified : dbase->modified)
  614.             strcat(buf, " (modified)");
  615.     }
  616.     print_button(w_info, buf);
  617. }
  618.  
  619.  
  620. /*-------------------------------------------------- variable pulldowns -----*/
  621. /*
  622.  * Read the current directory, the ./grokdir directory, the GROKDIR directory,
  623.  * and the preference directories, and collect all form files (*.f) and put
  624.  * them into the Form pulldown (pulldown #1). This is, of course, a flagrant
  625.  * violation of the Motif style guide.
  626.  */
  627.  
  628. #define MAXD    200        /* no more than 200 databases in pulldown */
  629. static char    *dbname[MAXD];    /* callback gets an index into this array */
  630. static char    *dbpath[MAXD];    /* path where form file was found */
  631. static Widget    dbwidget[MAXD];    /* button widgets, destroyed before remake */
  632.  
  633. static int append_to_dbase_pulldown(
  634.     long        nlines,        /* # of databases already in pulldown*/
  635.     char        *path)        /* directory name */
  636. {
  637.     char        *p;        /* for removing extension */
  638.     DIR        *dir;        /* open directory file */
  639.     struct dirent    *dp;        /* one directory entry */
  640.     int        num=0, i;
  641.  
  642.     path = resolve_tilde(path, 0);
  643.     if (!(dir = opendir(path)))
  644.         return(nlines);
  645.     while (nlines < MAXD-!num) {
  646.         if (!(dp = readdir(dir)))
  647.             break;
  648.         if (!(p = strrchr(dp->d_name, '.')) || strcmp(p, ".gf"))
  649.             continue;
  650.         *p = 0;
  651.         for (i=0; i < nlines; i++)
  652.             if (dbpath[i] && (pref.uniquedb ||
  653.                      !strcmp(dbpath[i], path))
  654.                       && !strcmp(dbname[i], dp->d_name))
  655.                     break;
  656.         if (i < nlines)
  657.             continue;
  658. #        ifndef NOMSEP
  659.         if (!num++ && nlines)
  660.             dbwidget[nlines++] = XtCreateManagedWidget(" ",
  661.                 xmSeparatorWidgetClass, dbpulldown, NULL, 0);
  662. #        endif
  663.         dbname[nlines] = mystrdup(dp->d_name);
  664.         dbpath[nlines] = mystrdup(path);
  665.         dbwidget[nlines] = XtCreateManagedWidget(dp->d_name,
  666.             xmPushButtonGadgetClass, dbpulldown, NULL, 0);
  667.         XtAddCallback(dbwidget[nlines], XmNactivateCallback,
  668.             (XtCallbackProc)dbase_pulldown, (XtPointer)nlines);
  669.         nlines++;
  670.     }
  671.     (void)closedir(dir);
  672.     return(nlines);
  673. }
  674.  
  675.  
  676. void remake_dbase_pulldown(void)
  677. {
  678.     int        n;
  679.     char        path[1024], *env;
  680.  
  681.     for (n=0; n < MAXD; n++) {
  682.         if (dbwidget[n])
  683.             XtDestroyWidget(dbwidget[n]);
  684.         if (dbpath[n])
  685.             free((void *)dbpath[n]);
  686.         if (dbname[n])
  687.             free((void *)dbname[n]);
  688.         dbwidget[n] = 0;
  689.         dbname[n] = 0;
  690.         dbpath[n] = 0;
  691.     }
  692.     sprintf(path, "%s/grokdir", LIB);
  693.     env = getenv("GROK_FORM");
  694.     n = append_to_dbase_pulldown(0, env ? env : "./");
  695.     n = append_to_dbase_pulldown(n, "./grokdir");
  696.     n = append_to_dbase_pulldown(n, GROKDIR);
  697.     n = append_to_dbase_pulldown(n, path);
  698. #ifdef XmNtearOffModel
  699.     XtVaSetValues(dbpulldown, XmNtearOffModel, XmTEAR_OFF_ENABLED, NULL);
  700. #endif
  701. }
  702.  
  703.  
  704. /*
  705.  * After a database was loaded, there is a section list in the dbase struct.
  706.  * Present it in a pulldown if there are at least two sections.
  707.  */
  708.  
  709. #define MAXSC  200        /* no more than 200 sections in pulldown */
  710. static Widget  scwidget[MAXSC];    /* button widgets, destroyed before remake */
  711.  
  712. void remake_section_pulldown(void)
  713. {
  714.     int        maxn;
  715.     long        n;
  716.  
  717.     for (n=0; n < MAXSC; n++) {
  718.         if (scwidget[n])
  719.             XtDestroyWidget(scwidget[n]);
  720.         scwidget[n] = 0;
  721.     }
  722.     if (!curr_card || !curr_card->dbase || curr_card->form->proc) {
  723.         XtVaSetValues(sectpdcall, XmNsensitive, FALSE, 0);
  724.         return;
  725.     }
  726.     maxn = curr_card->dbase->havesects ? curr_card->dbase->nsects + 2 : 1;
  727.     if (maxn > MAXSC)
  728.         maxn = MAXSC;
  729.     for (n=0; n < maxn; n++) {
  730.         scwidget[n] = XtCreateManagedWidget(
  731.                 n == maxn-1 ? "New ..." :
  732.                 n == 0      ? "All"     :
  733.                     section_name(curr_card->dbase, n-1),
  734.                 xmPushButtonGadgetClass, sectpulldown, NULL,0);
  735.         XtAddCallback(scwidget[n], XmNactivateCallback,
  736.                 (XtCallbackProc)section_pulldown,(XtPointer)n);
  737.     }
  738.     XtVaSetValues(sectpdcall, XmNsensitive, TRUE, 0);
  739. #ifdef XmNtearOffModel
  740.     XtVaSetValues(sectpulldown, XmNtearOffModel, XmTEAR_OFF_ENABLED, NULL);
  741. #endif
  742.     remake_section_popup(TRUE);
  743. }
  744.  
  745.  
  746. /*
  747.  * Put all the default queries in the form into the Query pulldown (pulldown
  748.  * #3). Add a default query "All" at the top. This is yet another violation
  749.  * of the Motif style guide. So what.
  750.  */
  751.  
  752. #define MAXQ    100        /* no more than 100 queries in pulldown */
  753. static Widget    qwidget[MAXQ];    /* button widgets, destroyed before remake */
  754.  
  755. void remake_query_pulldown(void)
  756. {
  757.     DQUERY        *dq;        /* current query entry */
  758.     long        i;        /* # of queries in pulldown */
  759.     int        n;        /* max # of lines in pulldown */
  760.  
  761.     for (n=0; n < MAXQ; n++) {
  762.         if (qwidget[n])
  763.             XtDestroyWidget(qwidget[n]);
  764.         qwidget[n] = 0;
  765.     }
  766.     if (!curr_card || !curr_card->form)
  767.         return;
  768.  
  769.     qwidget[0] = XtCreateManagedWidget("All",
  770.             xmPushButtonGadgetClass, qpulldown, NULL, 0);
  771.     XtAddCallback(qwidget[0], XmNactivateCallback,
  772.             (XtCallbackProc)query_pulldown, (XtPointer)0);
  773.     n = curr_card->form->nqueries > MAXQ ? MAXQ
  774.                          : curr_card->form->nqueries;
  775.     for (i=0; i < n; i++) {
  776.         dq = &curr_card->form->query[i];
  777.         if (dq->suspended || !dq->name || !dq->query)
  778.             continue;
  779.         qwidget[i+1] = XtCreateManagedWidget(dq->name,
  780.                 xmPushButtonGadgetClass, qpulldown, NULL, 0);
  781.         XtAddCallback(qwidget[i+1], XmNactivateCallback,
  782.                 (XtCallbackProc)query_pulldown,
  783.                 (XtPointer)(i+1));
  784.     }
  785. #ifdef XmNtearOffModel
  786.     XtVaSetValues(qpulldown, XmNtearOffModel, XmTEAR_OFF_ENABLED, NULL);
  787. #endif
  788. }
  789.  
  790.  
  791. /*
  792.  * put a new option popup menu on the card's Section button
  793.  */
  794.  
  795. static void    remake_popup(void);
  796. static Widget    *pwidgets;        /* label widgets in popup */
  797. static int    pnwidgets;        /* number of widgets in pwidgets[] */
  798.  
  799. void remake_section_popup(
  800.     BOOL        newsects)    /* did the section list change? */
  801. {
  802. #ifdef XmCSimpleOptionMenu
  803.     Arg        args[2];
  804.  
  805.     if (newsects)
  806.         remake_popup();
  807.  
  808.     if (!curr_card    || !curr_card->dbase
  809.             ||  curr_card->row < 0
  810.             ||  curr_card->row >= curr_card->dbase->nrows
  811.             || !curr_card->dbase->havesects) {
  812.         if (w_sect) {
  813.             XtSetArg(args[0], XmNsensitive, FALSE);
  814.             XtSetValues(w_sect, args, 1);
  815.             XtSetValues(w_del, args, 1);
  816.             XtSetValues(XmOptionButtonGadget(w_sect), args, 1);
  817.         }
  818.         return;
  819.     }
  820.     if (w_sect) {
  821.         XtSetArg(args[0], XmNsensitive, TRUE);
  822.         XtSetArg(args[1], XmNmenuHistory, pwidgets[
  823.             curr_card->dbase->row[curr_card->row]->section]);
  824.         XtSetValues(w_sect, args, 2);
  825.         XtSetValues(w_del, args, 1);
  826.         XtSetValues(XmOptionButtonGadget(w_sect), args, 1);
  827.     }
  828. #endif
  829. }
  830.  
  831.  
  832. static void remake_popup(void)
  833. {
  834. #ifdef XmCSimpleOptionMenu
  835.     static Widget    popup;        /* the popup menu */
  836.     XmString    str;        /* for labels in pwidgets, temp */
  837.     Arg        args[20];    /* for option menu creation */
  838.     int        i, n;
  839.  
  840.     if (w_sect) {
  841.         XtDestroyWidget(w_sect);
  842.         w_sect = 0;
  843.     }
  844.     if (popup) {
  845.         XtDestroyWidget(popup);
  846.         popup = 0;
  847.     }
  848.     if (pwidgets) {
  849.         for (i=0; i < pnwidgets; i++)
  850.             XtDestroyWidget(pwidgets[i]);
  851.         free(pwidgets);
  852.         pwidgets = 0;
  853.     }
  854.     pnwidgets = curr_card->dbase->nsects;
  855.     if (pnwidgets < 2) {
  856.         pnwidgets = 0;
  857.         return;
  858.     }
  859.     if (!(pwidgets = malloc(sizeof(Widget) * pnwidgets)))
  860.         return;
  861.  
  862.     popup = XmCreatePulldownMenu(form, "pulldown", NULL, 0);
  863.     str = XmStringCreateSimple("");
  864.     n = 0;
  865.     XtSetArg(args[n], XmNbottomAttachment,    XmATTACH_OPPOSITE_WIDGET); n++;
  866.     XtSetArg(args[n], XmNbottomWidget,    w_del);            n++;
  867.     XtSetArg(args[n], XmNmarginHeight,    0);            n++;
  868.     XtSetArg(args[n], XmNleftAttachment,    XmATTACH_WIDGET);    n++;
  869.     XtSetArg(args[n], XmNleftWidget,    w_del);            n++;
  870.     XtSetArg(args[n], XmNwidth,        60);            n++;
  871.     XtSetArg(args[n], XmNhighlightThickness,1);            n++;
  872.     XtSetArg(args[n], XmNsubMenuId,        popup);            n++;
  873.     XtSetArg(args[n], XmNlabelString,    str);            n++;
  874.     w_sect = XmCreateOptionMenu(form, "sectoption", args, n);
  875.     XtAddCallback(w_sect, XmNhelpCallback,
  876.             (XtCallbackProc)help_callback, (XtPointer)"sect");
  877.     XmStringFree(str);
  878.  
  879.     for (i=0; i < pnwidgets; i++) {
  880.         XtSetArg(args[0], XmNsensitive,
  881.                 !curr_card->dbase->sect[i].rdonly);
  882.         pwidgets[i] = XtCreateManagedWidget(
  883.                 section_name(curr_card->dbase, i),
  884.                 xmPushButtonGadgetClass, popup, NULL, 0);
  885.         XtAddCallback(pwidgets[i], XmNactivateCallback,
  886.                 (XtCallbackProc)sect_callback, (XtPointer)i);
  887.     }
  888.     XtManageChild(w_sect);
  889. #endif
  890. }
  891.  
  892.  
  893. /*
  894.  * Put all the columns in the summary into the Sort pulldown (pulldown #2),
  895.  * except those that have the nosort flag set.
  896.  * What you hear weeping is the author of the Motif style guide.
  897.  */
  898.  
  899. #define MAXS    100        /* no more than 100 criteria in pulldown */
  900. static Widget    swidget[MAXS];    /* button widgets, destroyed before remake */
  901.  
  902. void remake_sort_pulldown(void)
  903. {
  904.     int        sort_col[MAXS];    /* column for each pulldown item */
  905.     char        buf[256];    /* pulldown line text */
  906.     register ITEM    *item;        /* scan items for sortable columns */
  907.     int        i;        /* item counter */
  908.     int        j;        /* skip redundant choice items */
  909.     int        n;        /* # of lines in pulldown */
  910.  
  911.     for (n=0; n < MAXS; n++) {
  912.         if (swidget[n])
  913.             XtDestroyWidget(swidget[n]);
  914.         swidget[n] = 0;
  915.     }
  916.     if (!curr_card || !curr_card->form || !curr_card->dbase)
  917.         return;
  918.  
  919.     for (n=i=0; i < curr_card->form->nitems; i++) {
  920.         item = curr_card->form->items[i];
  921.         if (!IN_DBASE(item->type) || item->nosort)
  922.             continue;
  923.         for (j=0; j < n; j++)
  924.             if (item->column == sort_col[j])
  925.                 break;
  926.         if (j < n)
  927.             continue;
  928.         sprintf(buf, "by %.200s",
  929.             item->type == IT_CHOICE ? item->name : item->label);
  930.         if (buf[j = strlen(buf)-1] == ':')
  931.             buf[j] = 0;
  932.         swidget[n] = XtCreateManagedWidget(buf,
  933.                 xmPushButtonGadgetClass, sortpulldown, NULL,0);
  934.         XtAddCallback(swidget[n], XmNactivateCallback,
  935.                 (XtCallbackProc)sort_pulldown,
  936.                 (XtPointer)item->column);
  937.         sort_col[n++] = item->column;
  938.     }
  939. #ifdef XmNtearOffModel
  940.     XtVaSetValues(sortpulldown, XmNtearOffModel, XmTEAR_OFF_ENABLED,NULL);
  941. #endif
  942. }
  943.  
  944.  
  945. /*-------------------------------------------------- switch databases -------*/
  946. /*
  947.  * switch main window to new database. This happens on startup if a form
  948.  * name is given on the command line, and whenever a new form name is chosen
  949.  * from the database pulldown. ".gf" is appended to formname if necessary. If
  950.  * formname == 0, kill the current form and don't switch to a new one. Sort
  951.  * the new database by the default sort criterion if there is one.
  952.  */
  953.  
  954. void switch_form(
  955.     char        *formname)    /* new form name */
  956. {
  957.     char        name[1024], *p;    /* capitalized formname */
  958.     Arg        args[5];
  959.     int        i;
  960.  
  961.     if (curr_card) {
  962.         if (prev_form)
  963.             free(prev_form);
  964.         prev_form = 0;
  965.         if (curr_card->form)
  966.             prev_form = mystrdup(curr_card->form->name);
  967.         destroy_card_menu(curr_card);
  968.         if (curr_card->dbase) {
  969.             if (curr_card->dbase->modified &&
  970.                !curr_card->dbase->rdonly &&
  971.                !curr_card->form->rdonly)
  972.                 if (!write_dbase(curr_card->dbase,
  973.                          curr_card->form, FALSE))
  974.                     return;
  975.             dbase_delete(curr_card->dbase);
  976.             free((void *)curr_card->dbase);
  977.         }
  978.         if (curr_card->form) {
  979.             form_delete(curr_card->form);
  980.             free((void *)curr_card->form);
  981.         }
  982.         free((void *)curr_card);
  983.         curr_card = 0;
  984.     }
  985.     if (formname && *formname) {
  986.         FORM  *form  = form_create();
  987.         DBASE *dbase = dbase_create();
  988.         if (read_form(form, formname))
  989.             (void)read_dbase(dbase, form,
  990.                     form->dbase ? form->dbase : formname);
  991.  
  992.         curr_card = create_card_menu(form, dbase, w_card);
  993.         curr_card->form  = form;
  994.         curr_card->dbase = dbase;
  995.         curr_card->row   = 0;
  996.         col_sorted_by    = 0;
  997.         for (i=0; i < form->nitems; i++)
  998.             if (form->items[i]->defsort) {
  999.                 dbase_sort(curr_card, form->items[i]->column);
  1000.                 break;
  1001.             }
  1002.         if (form->autoquery >= 0 && form->autoquery < form->nqueries)
  1003.             query_any(curr_card,
  1004.                   form->query[form->autoquery].query);
  1005.         else
  1006.             query_all(curr_card);
  1007.         create_summary_menu(curr_card, w_summary, mainwindow);
  1008.  
  1009.         strcpy(name, formname);
  1010.         if (*name >= 'a' && *name <= 'z')
  1011.             *name += 'A' - 'a';
  1012.         if (p = strrchr(name, '.'))
  1013.             *p = 0;
  1014.         if (p = strrchr(name, '/'))
  1015.             p++;
  1016.         else
  1017.             p = name;
  1018.         if (toplevel) {
  1019.             XtVaSetValues(toplevel, XmNiconName, p, NULL);
  1020.             fillout_card(curr_card, FALSE);
  1021.         }
  1022.     } else
  1023.         if (toplevel)
  1024.             XtVaSetValues(toplevel, XmNiconName, "None", NULL);
  1025.  
  1026.     if (toplevel) {
  1027.         XtSetArg(args[0], XmNsensitive, formname != 0);
  1028.         XtSetValues(w_left,  args, 1);
  1029.         XtSetValues(w_right, args, 1);
  1030.         XtSetValues(w_del,   args, 1);
  1031.         XtSetValues(w_dup,   args, 1);
  1032.         XtSetValues(w_new,   args, 1);
  1033.  
  1034.         remake_query_pulldown();
  1035.         remake_sort_pulldown();
  1036.         remake_section_pulldown();    /* also sets w_sect, w_del */
  1037.         resize_mainwindow();
  1038.         if (curr_card && curr_card->dbase)
  1039.             curr_card->dbase->modified = FALSE;
  1040.         print_info_line();
  1041.     }
  1042. }
  1043.  
  1044.  
  1045. /*-------------------------------------------------- callbacks --------------*/
  1046. /*
  1047.  * some item in one of the menu bar pulldowns was pressed. All of these
  1048.  * routines are direct X callbacks.
  1049.  */
  1050.  
  1051. static void rambo_quit(void) { exit(0); }
  1052.  
  1053. /*ARGSUSED*/
  1054. static void file_pulldown(
  1055.     Widget                widget,
  1056.     int                item,
  1057.     XmToggleButtonCallbackStruct    *data)
  1058. {
  1059.     card_readback_texts(curr_card, -1);
  1060.     switch (item) {
  1061.       case 0:                        /* print */
  1062.         create_print_popup();
  1063.         break;
  1064.  
  1065.       case 1:                        /* preference*/
  1066.         create_preference_popup();
  1067.         break;
  1068.  
  1069.       case 3:                        /* about */
  1070.         create_about_popup();
  1071.         break;
  1072.  
  1073.       case 4:                        /* save */
  1074.         if (curr_card && curr_card->form && curr_card->dbase)
  1075.             if (curr_card->form->rdonly)
  1076.                 create_error_popup(mainwindow, 0,
  1077.                    "Database is marked read-only in the form");
  1078.             else
  1079.                 (void)write_dbase(curr_card->dbase,
  1080.                           curr_card->form, TRUE);
  1081.         else
  1082.             create_error_popup(mainwindow,0,"No database to save");
  1083.         print_info_line();
  1084.         break;
  1085.  
  1086.       case 5:                        /* quit */
  1087.         if (curr_card &&  curr_card->dbase
  1088.                   &&  curr_card->dbase->modified
  1089.                   && !curr_card->form->rdonly
  1090.                   && !write_dbase(curr_card->dbase,
  1091.                           curr_card->form, FALSE))
  1092.             break;
  1093.         exit(0);
  1094.  
  1095.       case 6:                        /* rambo quit*/
  1096.         if (curr_card &&  curr_card->dbase
  1097.                   && !curr_card->dbase->rdonly
  1098.                   &&  curr_card->dbase->modified
  1099.                   && !curr_card->form->rdonly)
  1100.             create_query_popup(mainwindow, rambo_quit, "rambo",
  1101.                 "OK to discard changes and quit?");
  1102.         else
  1103.             exit(0);
  1104.     }
  1105. }
  1106.  
  1107.  
  1108. /*ARGSUSED*/
  1109. static void newform_pulldown(
  1110.     Widget                widget,
  1111.     int                item,
  1112.     XmToggleButtonCallbackStruct    *data)
  1113. {
  1114.     card_readback_texts(curr_card, -1);
  1115.     switch (item) {
  1116.       case 0:                        /* current */
  1117.         if (curr_card && curr_card->form) {
  1118.             if (curr_card->dbase        &&
  1119.                !curr_card->dbase->rdonly    &&
  1120.                 curr_card->dbase->modified    &&
  1121.                !curr_card->form->rdonly    &&
  1122.                !write_dbase(curr_card->dbase,
  1123.                     curr_card->form, FALSE))
  1124.                         return;
  1125.             create_formedit_window(curr_card->form, FALSE, FALSE);
  1126.         } else
  1127.             create_error_popup(toplevel, 0,
  1128.              "Please choose database to edit\nfrom Database pulldown");
  1129.         break;
  1130.  
  1131.       case 1:                        /* new */
  1132.         switch_form(0);
  1133.         create_formedit_window(0, FALSE, TRUE);
  1134.         break;
  1135.  
  1136.       case 2:                        /* clone */
  1137.         if (curr_card && curr_card->form) {
  1138.             if (curr_card->dbase        &&
  1139.                !curr_card->dbase->rdonly    &&
  1140.                 curr_card->dbase->modified    &&
  1141.                !curr_card->form->rdonly    &&
  1142.                !write_dbase(curr_card->dbase,
  1143.                     curr_card->form, FALSE))
  1144.                         return;
  1145.             create_formedit_window(curr_card->form, TRUE, TRUE);
  1146.         } else
  1147.             create_error_popup(toplevel, 0,
  1148.             "Please choose database from Database pulldown first");
  1149.         break;
  1150.     }
  1151. }
  1152.  
  1153.  
  1154. /*ARGSUSED*/
  1155. static void help_pulldown(
  1156.     Widget                widget,
  1157.     int                item,
  1158.     XmToggleButtonCallbackStruct    *data)
  1159. {
  1160.     Cursor                cursor;
  1161.     Widget                w;
  1162.  
  1163.     switch (item) {
  1164.       case 0:                        /* context */
  1165.         cursor = XCreateFontCursor(display, XC_question_arrow);
  1166.         if (w = XmTrackingLocate(mainwindow, cursor, False)) {
  1167.             data->reason = XmCR_HELP;
  1168.             XtCallCallbacks(w, XmNhelpCallback, &data);
  1169.         }
  1170.         XFreeCursor(display, cursor);
  1171.         break;
  1172.  
  1173.       case 1:                        /* database */
  1174.         create_dbase_info_popup(curr_card);
  1175.         break;
  1176.  
  1177.       case 2:                        /* intro */
  1178.         help_callback(mainwindow, "intro");
  1179.         break;
  1180.  
  1181.       case 3:                        /* help */
  1182.         help_callback(mainwindow, "help");
  1183.         break;
  1184.  
  1185.       case 4:                        /* trouble */
  1186.         help_callback(mainwindow, "trouble");
  1187.         break;
  1188.  
  1189.       case 5:                        /* files */
  1190.         help_callback(mainwindow, "files");
  1191.         break;
  1192.  
  1193.       case 6:                        /* expr */
  1194.         help_callback(mainwindow, "grammar");
  1195.         break;
  1196.  
  1197.       case 7:                        /* resources */
  1198.         help_callback(mainwindow, "resources");
  1199.         break;
  1200.     }
  1201. }
  1202.  
  1203.  
  1204. /*ARGSUSED*/
  1205. static void dbase_pulldown(
  1206.     Widget                widget,
  1207.     int                item,
  1208.     XmToggleButtonCallbackStruct    *data)
  1209. {
  1210.     char                path[1024];
  1211.  
  1212.     card_readback_texts(curr_card, -1);
  1213.     sprintf(path, "%s/%s.gf", dbpath[item], dbname[item]);
  1214.     switch_form(path);
  1215.     remake_dbase_pulldown();
  1216. }
  1217.  
  1218.  
  1219. /*ARGSUSED*/
  1220. static void section_pulldown(
  1221.     Widget                widget,
  1222.     int                item,
  1223.     XmToggleButtonCallbackStruct    *data)
  1224. {
  1225.     card_readback_texts(curr_card, -1);
  1226.     if (item == curr_card->dbase->nsects+1 || item == MAXSC-1 ||
  1227.                     !curr_card->dbase->havesects)
  1228.         create_newsect_popup();
  1229.     else {
  1230.         curr_card->dbase->currsect = item-1;
  1231.         query_all(curr_card);
  1232.         create_summary_menu(curr_card, w_summary, mainwindow);
  1233.  
  1234.         curr_card->row = curr_card->query ? curr_card->query[0]
  1235.                           : curr_card->dbase->nrows;
  1236.         fillout_card(curr_card, FALSE);
  1237.     }
  1238. }
  1239.  
  1240.  
  1241. /*ARGSUSED*/
  1242. static void query_pulldown(
  1243.     Widget                widget,
  1244.     int                item,
  1245.     XmToggleButtonCallbackStruct    *data)
  1246. {
  1247.     print_info_line();
  1248.     if (!curr_card || !curr_card->dbase || !curr_card->dbase->nrows)
  1249.         return;
  1250.     card_readback_texts(curr_card, -1);
  1251.     if (item == 0)
  1252.         query_all(curr_card);
  1253.  
  1254.     else if (*curr_card->form->query[item-1].query == '/') {
  1255.         char *query = curr_card->form->query[item-1].query;
  1256.         char *string = XtMalloc(strlen(query));
  1257.         strcpy(string, query+1);
  1258.         append_search_string(string);
  1259.         query_search(curr_card, query+1);
  1260.     } else {
  1261.         char *query = curr_card->form->query[item-1].query;
  1262.         if (pref.query2search) {
  1263.             char *string = XtMalloc(strlen(query)+1);
  1264.             strcpy(string, query);
  1265.             append_search_string(string);
  1266.         }
  1267.         query_eval(curr_card, query);
  1268.     }
  1269.     create_summary_menu(curr_card, w_summary, mainwindow);
  1270.  
  1271.     curr_card->row = curr_card->query ? curr_card->query[0]
  1272.                       : curr_card->dbase->nrows;
  1273.     fillout_card(curr_card, FALSE);
  1274. }
  1275.  
  1276.  
  1277. /*ARGSUSED*/
  1278. static void sort_pulldown(
  1279.     Widget                widget,
  1280.     int                item,
  1281.     XmToggleButtonCallbackStruct    *data)
  1282. {
  1283.     card_readback_texts(curr_card, -1);
  1284.     dbase_sort(curr_card, item);
  1285.     create_summary_menu(curr_card, w_summary, mainwindow);
  1286.     curr_card->row = curr_card->query ? curr_card->query[0]
  1287.                       : curr_card->dbase->nrows;
  1288.     fillout_card(curr_card, FALSE);
  1289. }
  1290.  
  1291.  
  1292. /*
  1293.  * search for a search string, or for matching expressions. Put matching
  1294.  * cards into the summary. The string is an expression if it begins with
  1295.  * '(' or '{', or a search string otherwise. This is called from the
  1296.  * switch statement in expressions too.
  1297.  */
  1298.  
  1299. /*ARGSUSED*/
  1300. void search_cards(
  1301.     CARD                *card,
  1302.     char                *string)
  1303. {
  1304.     if (!string || !*string)
  1305.         return;
  1306.     print_info_line();
  1307.     card_readback_texts(card, -1);
  1308.     if (!card || !card->dbase || !card->dbase->nrows)
  1309.         return;
  1310.     query_any(card, string);
  1311.     create_summary_menu(card, w_summary, mainwindow);
  1312.     card->row = card->query ? card->query[0] : card->dbase->nrows;
  1313.     fillout_card(card, FALSE);
  1314. }
  1315.  
  1316.  
  1317. /*
  1318.  * Searches. When a search string is entered, read it from the button and
  1319.  * search for it. The arrows put a string from the history array into the
  1320.  * button, where it can be searched for. The query pulldown can append a
  1321.  * string to the history if pref.query2search is on.
  1322.  */
  1323.  
  1324. static char    *history[NHIST];
  1325. static int    s_curr, s_offs;
  1326.  
  1327. /*ARGSUSED*/
  1328. static void search_callback(
  1329.     Widget                widget,
  1330.     int                inc,
  1331.     XmToggleButtonCallbackStruct    *data)
  1332. {
  1333.     if (inc) {
  1334.         int o = s_offs + inc;
  1335.         if (o > -NHIST && o <= 0 && history[(s_curr + o) % NHIST]) {
  1336.             s_offs = o;
  1337.             print_text_button_s(w_search,
  1338.                     history[(s_curr + o + NHIST) % NHIST]);
  1339.         }
  1340.         append_search_string(0);
  1341.     } else {
  1342.         char *string = XmTextGetString(w_search);
  1343.         search_cards(curr_card, string);
  1344.         append_search_string(string);
  1345.     }
  1346. }
  1347.  
  1348. static void append_search_string(
  1349.     char        *text)
  1350. {
  1351.     Arg        arg;
  1352.  
  1353.     if (text) {
  1354.         print_text_button_s(w_search, text);
  1355.         s_offs = 0;
  1356.         if (history[s_curr] && *history[s_curr]
  1357.                     && strcmp(history[s_curr], text))
  1358.             s_curr = (s_curr + 1) % NHIST;
  1359.         if (history[s_curr])
  1360.             XtFree(history[s_curr]);
  1361.         history[s_curr] = text;
  1362.     }
  1363.     XtSetArg(arg, XmNsensitive, s_offs < 0);
  1364.     XtSetValues(w_next, &arg, 1);
  1365.     XtSetArg(arg, XmNsensitive, s_offs > -NHIST+1 &&
  1366.                 history[(s_curr + s_offs + NHIST -1) % NHIST]);
  1367.     XtSetValues(w_prev, &arg, 1);
  1368. }
  1369.  
  1370.  
  1371. /*
  1372.  * One of the letter buttons (a..z, misc, all) was pressed. Do the appropriate
  1373.  * search and display the result in the summary.
  1374.  */
  1375.  
  1376. /*ARGSUSED*/
  1377. static void letter_callback(
  1378.     Widget                widget,
  1379.     int                letter,
  1380.     XmToggleButtonCallbackStruct    *cbs)
  1381. {
  1382.     if (!curr_card || !curr_card->dbase || !curr_card->dbase->nrows)
  1383.         return;
  1384.     card_readback_texts(curr_card, -1);
  1385.     query_letter(curr_card, letter);
  1386.     create_summary_menu(curr_card, w_summary, mainwindow);
  1387.     curr_card->row = curr_card->query ? curr_card->query[0]
  1388.                       : curr_card->dbase->nrows;
  1389.     fillout_card(curr_card, FALSE);
  1390. }
  1391.  
  1392.  
  1393. /*
  1394.  * go forward or backward in the summary list if there is one. If there is
  1395.  * no summary, we must be adding cards and the user wants to see previously
  1396.  * added cards; step in the raw database in this case.
  1397.  */
  1398.  
  1399. /*ARGSUSED*/
  1400. static void pos_callback(
  1401.     Widget                widget,
  1402.     int                inc,
  1403.     XmToggleButtonCallbackStruct    *data)
  1404. {
  1405.     register CARD            *card = curr_card;
  1406.  
  1407.     card_readback_texts(curr_card, -1);
  1408.     if (card->qcurr + inc >= 0 &&
  1409.         card->qcurr + inc <  card->nquery) {
  1410.         card->qcurr += inc;
  1411.         card->row    = card->query[card->qcurr];
  1412.         fillout_card(card, FALSE);
  1413.         scroll_summary(card);
  1414.  
  1415.     } else if (card->nquery == 0 && card->row + inc >= 0
  1416.                      && card->row + inc <  card->dbase->nrows){
  1417.         card->row += inc;
  1418.         fillout_card(card, FALSE);
  1419.     }
  1420. }
  1421.  
  1422.  
  1423. /*
  1424.  * add a new card to the database. Since we don't know whether the new card
  1425.  * will be part of the summary when it is finished, remove the summary first.
  1426.  * When finished, find the first text input item and put the cursor into it.
  1427.  */
  1428.  
  1429. /*ARGSUSED*/
  1430. static void add_card(
  1431.     BOOL        dup)
  1432. {
  1433.     register CARD    *card = curr_card;
  1434.     register ITEM    *item;
  1435.     register DBASE    *dbase = card->dbase;
  1436.     int        i, s;
  1437.     int        oldrow = card->row;
  1438.  
  1439.     if (dbase->nsects > 1 && dbase->currsect < 0) {
  1440.         create_error_popup(toplevel, 0,
  1441.             "Choose a section from the section pulldown first");
  1442.         return;
  1443.     }
  1444.     card_readback_texts(card, -1);
  1445.     s = dbase->nsects > 1 ? dbase->currsect : 0;
  1446.     if (dbase->sect[s].rdonly) {
  1447.         create_error_popup(toplevel, 0,
  1448.                     "Section file\n\"%s\" is read-only",
  1449.                     dbase->sect[s].path);
  1450.         return;
  1451.     }
  1452.     if (!dbase_addrow(&card->row, dbase)) {
  1453.         create_error_popup(toplevel, errno,
  1454.                     "No memory for new database row");
  1455.         return;
  1456.     }
  1457.     if (dup)
  1458.         for (i=0; i < dbase->maxcolumns; i++)
  1459.             dbase_put(dbase, card->row, i,
  1460.                 mystrdup(dbase_get(dbase, oldrow, i)));
  1461.     else
  1462.         for (i=0; i < card->form->nitems; i++, item++) {
  1463.             item = card->form->items[i];
  1464.             if (IN_DBASE(item->type) && item->idefault)
  1465.                 dbase_put(dbase, card->row, item->column,
  1466.                      mystrdup(evaluate(card, item->idefault)));
  1467.         }
  1468.     query_none(card);
  1469.     create_summary_menu(card, w_summary, mainwindow);
  1470.     fillout_card(card, FALSE);
  1471.     scroll_summary(card);
  1472.     for (i=0; i < card->form->nitems; i++, item++) {
  1473.         item = card->form->items[i];
  1474.         if (item->type == IT_INPUT || item->type == IT_TIME
  1475.                        || item->type == IT_NOTE) {
  1476.             XmProcessTraversal(card->items[i].w0,
  1477.                         XmTRAVERSE_CURRENT);
  1478.             break;
  1479.         }
  1480.     }
  1481. }
  1482.  
  1483.  
  1484. /*ARGSUSED*/
  1485. static void new_callback(
  1486.     Widget                widget,
  1487.     XtPointer            null,
  1488.     XmToggleButtonCallbackStruct    *cbs)
  1489. {
  1490.     add_card(FALSE);
  1491. }
  1492.  
  1493.  
  1494. /*
  1495.  * duplicate a card. This does the same as New, but fills the card with
  1496.  * the same data as the current card.
  1497.  */
  1498.  
  1499. /*ARGSUSED*/
  1500. static void dup_callback(
  1501.     Widget                widget,
  1502.     XtPointer            null,
  1503.     XmToggleButtonCallbackStruct    *cbs)
  1504. {
  1505.     if (curr_card->row >= 0)
  1506.         add_card(TRUE);
  1507. }
  1508.  
  1509.  
  1510. /*
  1511.  * delete a card. Since the summary should not be deleted, the deleted card
  1512.  * must be removed from the query[] list. Since the query list contains the
  1513.  * row numbers of the cards it lists, and rows may get renumbered if one is
  1514.  * removed, row indices > deleted row must be decremented.
  1515.  */
  1516.  
  1517. /*ARGSUSED*/
  1518. static void del_callback(
  1519.     Widget                widget,
  1520.     XtPointer            null,
  1521.     XmToggleButtonCallbackStruct    *data)
  1522. {
  1523.     register CARD            *card = curr_card;
  1524.     register int            *p, *q;
  1525.     int                i, s;
  1526.  
  1527.     if (card->dbase->nrows == 0 || card->row >= card->dbase->nrows)
  1528.         return;
  1529.     s = card->dbase->row[card->row]->section;
  1530.     if (card->dbase->sect[s].rdonly) {
  1531.         create_error_popup(toplevel, 0,
  1532.                 "section file\n\"%s\" is read-only",
  1533.                 card->dbase->sect[s].path);
  1534.         return;
  1535.     }
  1536.     dbase_delrow(card->row, card->dbase);
  1537.     if (card->row >= card->dbase->nrows)
  1538.         card->row = card->dbase->nrows - 1;
  1539.     p = q = &card->query[0];
  1540.     for (i=0; i < card->nquery; i++, p++) {
  1541.         *q = *p - (*p > card->row);
  1542.         q += *p != card->row;
  1543.     }
  1544.     card->nquery -= p - q;
  1545.     print_info_line();
  1546.     fillout_card(card, FALSE);
  1547.     create_summary_menu(card, w_summary, mainwindow);
  1548. }
  1549.  
  1550.  
  1551. /*
  1552.  * assign new section to the current card (called from the option popup)
  1553.  */
  1554.  
  1555. /*ARGSUSED*/
  1556. static void sect_callback(
  1557.     Widget                widget,
  1558.     int                item,
  1559.     XmToggleButtonCallbackStruct    *data)
  1560. {
  1561.     register CARD            *card = curr_card;
  1562.     register SECTION        *sect = card->dbase->sect;
  1563.     int                old, new;
  1564.  
  1565.     old = card->dbase->row[card->row]->section;
  1566.     new = item;
  1567.     if (sect[old].rdonly)
  1568.         create_error_popup(toplevel, 0,
  1569.             "section file\n\"%s\" is read-only", sect[old].path);
  1570.     else if (sect[new].rdonly)
  1571.         create_error_popup(toplevel, 0,
  1572.             "section file\n\"%s\" is read-only", sect[new].path);
  1573.     else {
  1574.         sect[old].nrows--;
  1575.         sect[new].nrows++;
  1576.         sect[old].modified = TRUE;
  1577.         sect[new].modified = TRUE;
  1578.         card->dbase->row[card->row]->section = new;
  1579.         card->dbase->modified = TRUE;
  1580.         print_info_line();
  1581.     }
  1582.     remake_section_popup(FALSE);
  1583. }
  1584.